#include "rtsppusher.h"
#include"dlog.h"
#include"timesutil.h"

RtspPusher::RtspPusher()
{
    LogInfo("RtspPusher Create");
}

RtspPusher::~RtspPusher()
{
    deInit();
    LogInfo("~RtspPusher over");
}

///
/// ffmpeg的api执行过程中会回调这个函数
/// \brief decode_interrup_cb
/// \param ctx
/// \return false：继续阻塞【即继续本次ffmpeg的api调用】；true：退出阻塞【即异常结束本次ffmpeg的api调用】
///
///
static int decode_interrup_cb(void *ctx)
{
    RtspPusher *rtsp_pusher = (RtspPusher*)ctx;
    if(rtsp_pusher->isTimeout())
    {
        LogWarn("timeout:%dms", rtsp_pusher->getTimeout());
        return 1;
    }
    // 这行日志没必要打印，太频繁了
    LogDebug("block time:%dms", rtsp_pusher->getBlockTime());
    return 0;
}

RET_CODE RtspPusher::init(const Properties properties)
{
    _url = properties.GetProperty("url", "");
    _rtsp_transport = properties.GetProperty("rtsp_transport", "");
    _video_frame_duration = properties.GetProperty("video_frame_duration", 0);
    _audio_frame_duration = properties.GetProperty("audio_frame_duration", 0);
    _timeout = properties.GetProperty("timeout", 5000);//默认超时时间5s

    if(_url == "")
    {
        LogError("url is null");
        return RET_FAIL;
    }
    if(_rtsp_transport == "")
    {
        LogError("rtsp_rtansport is null, use udp or tcp");
        return RET_FAIL;
    }
    int ret = 0;
    char buf[512] = {0};
    // 初始化网络库
    ret = avformat_network_init();
    if(ret < 0)
    {
        av_strerror(ret, buf, sizeof(buf) - 1);
        LogError("avformat_network_init failed:%s", buf);
        return RET_FAIL;
    }
    // 分配输出流上下文
    ret = avformat_alloc_output_context2(&_fmt_ctx, NULL, "rtsp", _url.c_str());
    if(ret < 0)
    {
        av_strerror(ret, buf, sizeof(buf) - 1);
        LogError("avformat_alloc_output_context2 failed:%s", buf);
        return RET_FAIL;
    }
    // 设置传输方式
    ret = av_opt_set(_fmt_ctx->priv_data, "rtsp_transport", _rtsp_transport.c_str(), 0);
    if(ret < 0)
    {
        av_strerror(ret, buf, sizeof(buf) - 1);
        LogError("av_opt_set failed:%s", buf);
        return RET_FAIL;
    }
    // 设置ffmpeg的api中的回调，每次执行api会在ffmpeg的while循环调用该方法判断是否可以进行下一步
    _fmt_ctx->interrupt_callback.callback = decode_interrup_cb;
    _fmt_ctx->interrupt_callback.opaque = this;
    // 创建队列
    _queue = new PacketQueue(_audio_frame_duration, _video_frame_duration);
    if(!_queue)
    {
        LogError("new PacketQueue failed");
        return RET_ERR_OUTOFMEMORY;
    }
    return RET_OK;
}

void RtspPusher::deInit()
{
    if(_queue)
    {
        _queue->abort();
    }
    stop();
    if(_fmt_ctx)
    {
        avformat_free_context(_fmt_ctx);
    }
    if(_queue)
    {
        delete _queue;
        _queue = NULL;
    }
}

RET_CODE RtspPusher::push(AVPacket *pkt, MediaType media_type)
{
    int ret = _queue->push(pkt, media_type);
    if(ret < 0)
    {
        return RET_FAIL;
    }
    else
    {
        return RET_FAIL;
    }
}

RET_CODE RtspPusher::connect()
{
    // 如果一个流都没有，那就不用连接了
    if(!_audio_stream && !_video_stream)
    {
        return RET_FAIL;
    }
    LogInfo("connect to :%s", _url.c_str());
    resetTimeout(); //下一步要调用ffmepg的api函数了，需要更新计算超时时间的起始时间
    // 连接服务器
    int ret = avformat_write_header(_fmt_ctx, NULL);
    if(ret < 0)
    {
        char buf[512] = {0};
        av_strerror(ret, buf, sizeof(buf) - 1);
        LogError("avformat_write_header failed:%s", buf);
        return RET_FAIL;
    }
    LogInfo("avformat_write_header ok");
    return start();
}

RET_CODE RtspPusher::configVideoStream(const AVCodecContext *ctx)
{
    if(!_fmt_ctx)
    {
        LogError("fmt_ctx is null");
        return RET_FAIL;
    }
    if(!ctx)
    {
        LogError("ctx is null");
        return RET_FAIL;
    }
    // 添加视频流
    AVStream *vs = avformat_new_stream(_fmt_ctx, NULL);
    if(!vs)
    {
        LogError("avformat_new_stream failed");
        return RET_FAIL;
    }
    vs->codecpar->codec_tag = 0;
    // 从编码器中拷贝信息到视频流中
    avcodec_parameters_from_context(vs->codecpar, ctx);
    // 保存视频编码器、流
    _video_ctx = (AVCodecContext *)ctx;
    _video_stream = vs;
    _video_idx = vs->index; // 该index索引非常重要，用于区分音视频包
    return RET_OK;
}

RET_CODE RtspPusher::configAudioStream(const AVCodecContext *ctx)
{
    if(!_fmt_ctx)
    {
        LogError("fmt_ctx is null");
        return RET_FAIL;
    }
    if(!ctx)
    {
        LogError("ctx is null");
        return RET_FAIL;
    }
    // 添加音频流
    AVStream *vs = avformat_new_stream(_fmt_ctx, NULL);
    if(!vs)
    {
        LogError("avformat_new_stream failed");
        return RET_FAIL;
    }
    vs->codecpar->codec_tag = 0;
    // 从编码器中拷贝信息到音频流中
    avcodec_parameters_from_context(vs->codecpar, ctx);
    // 保存音频编码器、流
    _audio_ctx = (AVCodecContext *)ctx;
    _audio_stream = vs;
    _audio_idx = vs->index; // 该index索引非常重要，用于区分音视频包
    return RET_OK;
}

void RtspPusher::loop()
{
    LogInfo("Loop into");
    int ret = 0;
    AVPacket *pkt = NULL;
    MediaType media_type;
    // 循环取包数据
    while(true)
    {
        if(_request_abort)
        {
            LogInfo("abort request");
            break;
        }
        ret = _queue->popWithTimeout(&pkt, media_type, 2000);
        // 如果取到包数据了
        if(0 == ret)
        {
            if(_request_abort)
            {
                LogInfo("abort request");
                av_packet_free(&pkt);
                break;
            }
            switch (media_type) {
            case E_VIDEO_TYPE:
                ret = sendPacket(pkt, media_type);
                if(ret < 0)
                {
                    LogError("send video Packet failed");
                }
                av_packet_free(&pkt);
                break;
            case E_AUDIO_TYPE:
                ret = sendPacket(pkt, media_type);
                if(ret < 0)
                {
                    LogError("send audio Packet failed");
                }
                av_packet_free(&pkt);
                break;
            default:
                break;
            }
        }
    }
    resetTimeout(); //下一步要调用ffmepg的api函数了，需要更新计算超时时间的起始时间
    // 关闭服务器连接
    ret = av_write_trailer(_fmt_ctx);
    if(ret < 0)
    {
        char buf[512] = {0};
        av_strerror(ret, buf, sizeof(buf) - 1);
        LogError("av_write_trailer failed:%s", buf);
        return ;
    }
    LogInfo("Loop over");
}

bool RtspPusher::isTimeout()
{
    // 如果从调用api开始，到现在已经超过 timeout 这么旧的时间了，那么就代表超时了
    if(TimesUtil::GetTimeMillisecond() - _pre_time > _timeout)
    {
        return true;
    }
    return false;
}

void RtspPusher::resetTimeout()
{
    // 记录从此刻开始调用api
    _pre_time = TimesUtil::GetTimeMillisecond();
}

int RtspPusher::getTimeout()
{
    return _timeout;
}

int64_t RtspPusher::getBlockTime()
{
    return TimesUtil::GetTimeMillisecond() - _pre_time;
}

int RtspPusher::sendPacket(AVPacket *pkt, MediaType media_type)
{
    AVRational dst_time_base;// 音视频包本身的time_base，音频为赫兹，即 1/48000, 视频为帧率，即 1/25
    AVRational src_time_base = {1, 1000}; //本地采集的时间，单位ms
    if(E_VIDEO_TYPE == media_type)
    {
        pkt->stream_index = _video_idx;
        dst_time_base = _video_stream->time_base;
    }
    else if(E_AUDIO_TYPE == media_type)
    {
        pkt->stream_index = _audio_idx;
        dst_time_base = _audio_stream->time_base;
    }
    else
    {
        LogError("unkown media_type:%d", media_type);
        return -1;
    }
    // 重设pts，将包的pts转换为现实采集时间的pts，即假设是视频包，在dst_time_base中 pts 是2（帧），那么转换为现实采集时间后 pts 就是 80（ms）
    pkt->pts = av_rescale_q(pkt->pts, src_time_base, dst_time_base);
    pkt->duration = 0;
    resetTimeout(); //下一步要调用ffmepg的api函数了，需要更新计算超时时间的起始时间
    // 推流传输数据
    int ret = av_write_frame(_fmt_ctx, pkt);
    if(ret < 0)
    {
        char buf[512] = {0};
        av_strerror(ret, buf, sizeof(buf) - 1);
        LogError("av_write_frame failed:%s", buf);
        return -1;
    }
    return 0;
}
